package org.jeecg.modules.third.oapi.alct56.utils;

import zyb.org.bouncycastle.asn1.gm.GMNamedCurves;
import zyb.org.bouncycastle.asn1.x9.X9ECParameters;
import zyb.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import zyb.org.bouncycastle.crypto.InvalidCipherTextException;
import zyb.org.bouncycastle.crypto.engines.SM2Engine;
import zyb.org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import zyb.org.bouncycastle.crypto.params.*;
import zyb.org.bouncycastle.math.ec.ECPoint;
import zyb.org.bouncycastle.util.encoders.Hex;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class SM2Utils {

    private static String CHARSET_UTF8 = "UTF-8";

    public SM2Utils() {
    }

    public static String encryptByPublicKey(String data, String publicKey) throws UnsupportedEncodingException, InvalidCipherTextException {
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(hexString2byte(publicKey));
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
        byte[] in = data.getBytes(CHARSET_UTF8);
        byte[] arrayOfBytes = Base64.getEncoder().encode(sm2Engine.processBlock(in, 0, in.length));
        return Hex.toHexString(arrayOfBytes);
    }

    public static String decryptByPrivateKey(String encrtpyData, String privateKey) throws InvalidCipherTextException, UnsupportedEncodingException {
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        byte[] cipherDataByte = Base64.getDecoder().decode(Hex.decode(encrtpyData));
        BigInteger privateKeyD = new BigInteger(privateKey, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, privateKeyParameters);
        byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
        return new String(arrayOfBytes, CHARSET_UTF8);
    }

    public Sm2KeyVo getKeys() throws NoSuchAlgorithmException {
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
        keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
        BigInteger privatekey = ((ECPrivateKeyParameters)asymmetricCipherKeyPair.getPrivate()).getD();
        String privateKeyHex = privatekey.toString(16);
        ECPoint ecPoint = ((ECPublicKeyParameters)asymmetricCipherKeyPair.getPublic()).getQ();
        String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
        Sm2KeyVo sm2KeyVo = new Sm2KeyVo();
        sm2KeyVo.setPrivateKey(privateKeyHex);
        sm2KeyVo.setPublicKey(publicKeyHex);
        return sm2KeyVo;
    }

    public static byte[] hexString2byte(String hexString) {
        if (null != hexString && hexString.length() % 2 == 0 && !hexString.contains("null")) {
            byte[] bytes = new byte[hexString.length() / 2];

            for(int i = 0; i < hexString.length(); i += 2) {
                bytes[i / 2] = (byte)(Integer.parseInt(hexString.substring(i, i + 2), 16) & 255);
            }

            return bytes;
        } else {
            return null;
        }
    }

}
